/*
 * @(#)WorldGenerator.java     14/08/02
 * 
 * Author             Rick Wilson
 * Copyright (c) 2014 Rick Wilson
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of 'Taareekah' nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */



package worldgeneration;

//~--- non-JDK imports --------------------------------------------------------

import core.GameScriptManager;
import core.Main;

import parameters.Parameters;
import parameters.ParameterGroup;
import parameters.ParameterNode;

import groovy.lang.Binding;

import world.GenericTerrainMap;
import world.Terrain;
import world.World;

//~--- JDK imports ------------------------------------------------------------

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.management.JMException;

/**
 * Generates a new world based on a set of parameters.
 * @author rick
 */
public class WorldGenerator {

    /** Field description */
    private boolean	is_generated	= false;

    /** Field description */
    private GameScriptManager	gsm;

    /** Field description */
    private Terrain	terrain;

    /** Field description */
    private Parameters	parameters;

    /** Field description */
    private Parameters	biome_parameters;

    /**
     *
     * @param gsm The GameScriptManager
     * @throws Exception Doesnt
     */
    public WorldGenerator(GameScriptManager gsm) throws Exception {
	this.gsm	= gsm;
    }

    /**
     *
     * @return
     */
    public Parameters getWorldGenerationParameters() {
	return parameters;
    }

    /**
     *
     * @return
     */
    public boolean isGenerated() {
	return is_generated;
    }

    /**
     *
     * @return
     */
    public Terrain getTerrain() {
	return terrain;
    }

    /**
     *
     * @param name
     * @return
     */
    public boolean isMapGenerated(String name) {
	if (!hasMap(name)) {
	    return false;
	}

	try {
	    if (getMap(name).isLoaded()) {
		return true;
	    }
	} catch (Exception ex) {
	    return false;
	}

	return false;
    }

    /**
     *
     * @param name
     * @param value
     * @throws Exception
     */
    public void setMap(String name, GenericTerrainMap value) throws Exception {
	terrain.addTerrainMap(name, value);
    }

    /**
     *
     * @param name
     * @return
     */
    public boolean hasMap(String name) {
	if (!terrain.hasTerrainMap(name)) {
	    return false;
	}

	return true;
    }

    /**
     *
     * @param name
     * @return
     * @throws Exception
     */
    public GenericTerrainMap getMap(String name) throws Exception {
	if (!terrain.hasTerrainMap(name)) {
	    throw new JMException("Invalid map name");
	}

	return terrain.getTerrainMap(name);
    }

    /**
     * Method description
     *
     *
     * @return
     *
     * @throws Exception
     */
    private List<ParameterGroup> getGenerationScriptsInOrder() throws Exception {
	ParameterGroup		root		= (ParameterGroup) parameters.getParameterNode("generationscripts");
	List<ParameterGroup>	ret		= new ArrayList<>();
	int			num_scripts	= root.getChild("order").getNumChildren();

	for (int i = 0; i < num_scripts; i++) {
	    String	script_name	= root.getChild("order." + String.valueOf(i)).getValueAsString();

	    ret.add((ParameterGroup) root.getChild(script_name));
	}

	return ret;
    }

    /**
     * Method description
     *
     *
     * @throws Exception
     */
    private void runGenerationScripts() throws Exception {
	List<ParameterGroup>	parts	= getGenerationScriptsInOrder();

	for (ParameterGroup p : parts) {
	    try {
		String	cur_filename	= p.getChild("filename").getValueAsString();
		Binding	binding		= gsm.createBinding();

		binding.setVariable("worldgenerator", this);
		binding.setVariable("world", Main.getWorld());
		binding.setVariable("gomanager", Main.getWorld().getGOManager());
		binding.setVariable("scriptparameters", p);
		binding.setVariable("globalparameters", (ParameterGroup) parameters.getParameterNode(""));
		gsm.runScriptWithBinding(cur_filename, binding);
	    } catch (Exception e) {
		throw e;
	    }
	}
    }

    /**
     * Method description
     *
     *
     * @param nodes
     *
     * @throws Exception
     */
    private void parseAndAddBiomes(Collection<ParameterNode> nodes) throws Exception {
	for (ParameterNode p : nodes) {
	    if (!p.isGroup()) {
		continue;
	    }

	    ParameterGroup	g	= (ParameterGroup) p;

	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, g.toString());

	    if (!g.hasChild("color")) {
		Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, "Has no child color");
	    }

	    if (!g.hasChild("name")) {
		Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, "Has no child name");
	    }

	    if (terrain.getBiomeMap() == null) {
		Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, "Biome map null");
	    }

	    terrain.getBiomeMap().addBiomeType((ParameterGroup) p);
	}
    }

    /**
     *
     * @return
     */
    public long getSeed() {
	try {
	    return Long.parseLong(parameters.getParameterValueAsString("seed"));
	} catch (Exception ex) {
	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, null, ex);
	}

	return -1;
    }

    /**
     *
     * @return
     */
    public int getWorldSize() {
	try {
	    return Integer.parseInt(parameters.getParameterValueAsString("size"));
	} catch (Exception ex) {
	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, null, ex);
	}

	return -1;
    }

    /**
     *
     * @return
     */
    public String getWorldName() {
	try {
	    return parameters.getParameterValueAsString("worldname");
	} catch (Exception ex) {
	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, null, ex);
	}

	return null;
    }

    /**
     *
     * @param name
     * @throws Exception
     */
    public void generate(String name) throws Exception {
	if (is_generated) {
	    is_generated	= false;
	    terrain		= null;
	}

	try {
	    parameters	= Parameters.getParameterSet("general");
	    terrain	= new Terrain(getWorldSize());
	    Main.getWorld().setTerrain(terrain);

	    String	biome_params	= parameters.getParameterValueAsString("biomeparams");

	    biome_parameters	= Parameters.createParameterSet("biome", biome_params);
	    parseAndAddBiomes(biome_parameters.getParameterNode("").getChildren());
	} catch (Exception e) {
	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, "Couldnt load generator params", e);

	    throw e;
	}

	try {
	    runGenerationScripts();
	} catch (Exception e) {
	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, "Couldnt load script", e);

	    throw e;
	}

	is_generated	= true;
    }

    /**
     *
     * @param name
     * @param config
     * @throws Exception
     */
    public void generate(String name, String config) throws Exception {
	try {
	    Parameters.createParameterSet("general", config);
	    generate(name);
	} catch (Exception e) {
	    Logger.getLogger(WorldGenerator.class.getName()).log(Level.SEVERE, "Couldnt load generator params", e);

	    throw e;
	}
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
